package com.lenovo.lcdm.access.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lenovo.lcdm.access.dto.AccessPolicyDto;
import com.lenovo.lcdm.access.dto.MenuPolicyTreeDto;
import com.lenovo.lcdm.access.dto.UserInfoDto;
import com.lenovo.lcdm.access.entity.AccessMenu;
import com.lenovo.lcdm.access.entity.AccessPolicy;
import com.lenovo.lcdm.access.mapper.AccessPolicyMapper;
import com.lenovo.lcdm.access.service.AccessPolicyService;
import com.lenovo.lcdm.common.enums.CommonMsgEnum;
import com.lenovo.lcdm.common.exception.BusinessException;
import com.lenovo.lcdm.team.common.util.ContextUtil;
import com.lenovo.lcdm.team.dto.UserAuthDto;
import com.lenovo.lcdm.team.entity.TeamRole;
import com.lenovo.lcdm.team.entity.TeamUser;
import com.lenovo.lcdm.team.service.ITeamRoleService;
import com.lenovo.lcdm.team.service.ITeamUserService;
import com.lenovo.lcdm.access.util.ObjectUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* @author xujing30
* @description 针对表【access_policy】的数据库操作Service实现
* @createDate 2024-02-01 17:23:35
*/
@Service
public class AccessPolicyServiceImpl extends ServiceImpl<AccessPolicyMapper, AccessPolicy>
    implements AccessPolicyService{

    @Autowired
    @Lazy
    private AccessMenuServiceImpl menuService;

    @Autowired
    private ITeamRoleService teamRoleService;

    @Autowired
    private ITeamUserService userService;

    private static final String POLICY_READ = "Read";

    private static final String POLICY_EDIT = "Edit";

    private static final Long ROOT_MENU_ID = 0L;

    private static final Long ROOT_MENU_PARENT_ID = -1L;

    private static final String ROOT_MENU_NAME = "root";

    @Override
    public List<AccessPolicy> getByMenuId(Long menuId) {
        LambdaQueryWrapper<AccessPolicy> qw = new LambdaQueryWrapper<>();
        qw.eq(AccessPolicy::getMenuId, menuId);
        return list(qw);
    }

    @Override
    public void removeByMenuId(Long menuId) {
        LambdaQueryWrapper<AccessPolicy> qw = new LambdaQueryWrapper<>();
        qw.eq(AccessPolicy::getMenuId, menuId);
        remove(qw);
    }

    @Override
    public AccessPolicy saveAccessPolicy(AccessPolicyDto accessPolicyDto) {
        if(ObjectUtil.isAnyEmpty(accessPolicyDto.getMenuId(), accessPolicyDto.getRoleId(), accessPolicyDto.getPolicy())){
            throw new BusinessException("Parameter cannot be empty.", CommonMsgEnum.FAIL.getCode());
        }

        AccessPolicy oldAccess = getAccessPolicyByMenuAndGroup(accessPolicyDto.getMenuId(), accessPolicyDto.getRoleId());
        if(oldAccess != null){
            throw new BusinessException("Access policy exist.", CommonMsgEnum.FAIL.getCode());
        }
        AccessMenu menu = menuService.getById(accessPolicyDto.getMenuId());
        if(menu == null){
            throw new BusinessException("Menu not exist.", CommonMsgEnum.FAIL.getCode());
        }
        AccessPolicy accessPolicy = new AccessPolicy();
        BeanUtils.copyProperties(accessPolicyDto, accessPolicy);
        accessPolicy.setCreator(ContextUtil.getCurrentUser());
        accessPolicy.setCreateTime(LocalDateTime.now());
        accessPolicy.setModifier(ContextUtil.getCurrentUser());
        accessPolicy.setModifyTime(LocalDateTime.now());
        save(accessPolicy);
        //设值父节点权限
        setParentAccessPolicy(menu, accessPolicyDto.getRoleId());
        return accessPolicy;
    }

    private void setParentAccessPolicy(AccessMenu menu, Long roleId){
        Long parentId = menu.getParentId();
        AccessMenu parentMenu = menuService.getById(parentId);
        if(parentMenu != null && parentMenu.getParentId() != ROOT_MENU_PARENT_ID){
            AccessPolicy policy = this.getAccessPolicyByMenuAndGroup(parentId, roleId);
            if(policy == null){
                AccessPolicy accessPolicy = new AccessPolicy();
                accessPolicy.setMenuId(parentId);
                accessPolicy.setRoleId(roleId);
                accessPolicy.setPolicy(POLICY_READ);
                accessPolicy.setCreator(ContextUtil.getCurrentUser());
                accessPolicy.setCreateTime(LocalDateTime.now());
                accessPolicy.setModifier(ContextUtil.getCurrentUser());
                accessPolicy.setModifyTime(LocalDateTime.now());
                save(accessPolicy);
            }
            setParentAccessPolicy(parentMenu, roleId);
        }

    }

    @Override
    public AccessPolicy updateAccessPolicy(AccessPolicyDto accessPolicyDto) {
        if(ObjectUtil.isAnyEmpty(accessPolicyDto.getMenuId(), accessPolicyDto.getRoleId(), accessPolicyDto.getPolicy(), accessPolicyDto.getId())){
            throw new BusinessException("Parameter cannot be empty.", CommonMsgEnum.FAIL.getCode());
        }

        AccessPolicy oldAccess = getById(accessPolicyDto.getId());
        if(oldAccess == null){
            throw new BusinessException("Access policy does not exist.", CommonMsgEnum.FAIL.getCode());
        }

        AccessPolicy newAccess = getAccessPolicyByMenuAndGroup(accessPolicyDto.getMenuId(), accessPolicyDto.getRoleId());
        if(newAccess != null && newAccess.getId() != accessPolicyDto.getId()){
            throw new BusinessException("Authority for Menu-Role exist.", CommonMsgEnum.FAIL.getCode());
        }

        BeanUtils.copyProperties(accessPolicyDto, oldAccess);
        oldAccess.setModifier(ContextUtil.getCurrentUser());
        oldAccess.setModifyTime(LocalDateTime.now());
        saveOrUpdate(oldAccess);
        return oldAccess;
    }

    @Override
    public boolean deleteAccessPolicy(Long id) {
        AccessPolicy oldAccess = getById(id);
        if(oldAccess == null){
            throw new BusinessException("Access policy does not exist.", CommonMsgEnum.FAIL.getCode());
        }
        return removeById(id);
    }

    @Override
    public List<AccessPolicy> getAccessPolicyPage(Page page, String menuName) {
        LambdaQueryWrapper<AccessPolicy> qw = new LambdaQueryWrapper<>();
        if(StringUtils.isNotEmpty(menuName)){
            QueryWrapper<AccessMenu> menuQw = new QueryWrapper<>();
            menuQw.select("id").like("lower(name)", menuName.toLowerCase());
            List<Long> menuIds = menuService.listObjs(menuQw);
            qw.in(AccessPolicy::getMenuId, menuIds);
        }
        qw.orderByAsc(AccessPolicy::getRoleId);
        List<AccessPolicy> accessPolicies = list(page, qw);
        if(CollectionUtils.isNotEmpty(accessPolicies)) {
            List<TeamRole> roles = teamRoleService.listByIds(accessPolicies.stream().map(AccessPolicy::getRoleId).collect(Collectors.toList()));
            Map<Long, String> roleMap = roles.stream().collect(Collectors.toMap(TeamRole::getId, TeamRole::getRoleName));
            List<AccessMenu> menus = menuService.listByIds(accessPolicies.stream().map(AccessPolicy::getMenuId).collect(Collectors.toList()));
            Map<Long, String> menuMap = menus.stream().collect(Collectors.toMap(AccessMenu::getId, AccessMenu::getName));
            accessPolicies.forEach(policy -> {
                policy.setRoleName(roleMap.get(policy.getRoleId()));
                policy.setMenuName(menuMap.get(policy.getMenuId()));
            });
        }
        return accessPolicies;
    }

    @Override
    public AccessPolicy getAccessPolicyByMenuAndGroup(Long menuId, Long roleId) {
        if(ObjectUtil.isAnyEmpty(menuId, roleId)){
            throw new BusinessException("Parameter cannot be empty.", CommonMsgEnum.FAIL.getCode());
        }
        LambdaQueryWrapper<AccessPolicy> qw = new LambdaQueryWrapper<>();
        qw.eq(AccessPolicy::getMenuId, menuId).eq(AccessPolicy::getRoleId, roleId);
        return getOne(qw);
    }

    @Override
    public MenuPolicyTreeDto getUserMenuPolicyTree() {
        //组装菜单树
        MenuPolicyTreeDto menuPolicyTreeDto = new MenuPolicyTreeDto();
        menuPolicyTreeDto.setId(ROOT_MENU_ID);
        menuPolicyTreeDto.setName(ROOT_MENU_NAME);
        //根据用户查询角色
        UserAuthDto userAuthDto = userService.getUserAuthorities(ContextUtil.getCurrentUser(), true);
        List<Long> roles = userAuthDto.getRoles().stream().map(TeamRole::getId).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(roles)){
            return menuPolicyTreeDto;
        }
        //根据角色查询权限
        List<AccessPolicy> accessPolicies = this.getByRoles(roles);
        if(CollectionUtils.isEmpty(accessPolicies)){
            return menuPolicyTreeDto;
        }

        //获取每个菜单的最大权限
        Map<Long, List<AccessPolicy>> map = accessPolicies.stream().collect(Collectors.groupingBy(AccessPolicy::getMenuId));
        Map<Long, String> menuPolicy = new HashMap<>();
        map.keySet().forEach(key->{
            List<AccessPolicy> menuPolicies = map.get(key);
            if(CollectionUtils.isNotEmpty(menuPolicies)){
                List<String> policies = menuPolicies.stream().map(AccessPolicy::getPolicy).collect(Collectors.toList());
                String policy = "";
                if(policies.contains(POLICY_EDIT)){
                    policy = POLICY_EDIT;
                }else {
                    policy = POLICY_READ;
                }
                menuPolicy.put(key, policy);
            }
        });
        //获取菜单父子结构
        List<AccessMenu> menus = menuService.listByIds(map.keySet());
        Map<Long,List<AccessMenu>> menuParentMap = menus.stream().collect(Collectors.groupingBy(AccessMenu::getParentId));

        findChildren(menuPolicyTreeDto, ROOT_MENU_ID, menuParentMap, menuPolicy);

        return menuPolicyTreeDto;
    }

    /**
     * 查找菜单权限子树
     * @param menuTreeDto
     * @param parentId
     */
    private void findChildren(MenuPolicyTreeDto menuTreeDto, Long parentId, Map<Long,List<AccessMenu>> menuParentMap, Map<Long, String> menuPolicy) {
        List<AccessMenu> children = menuParentMap.get(parentId);
        if(CollectionUtils.isNotEmpty(children)){
            menuTreeDto.setChildren(children.stream().map(item -> {
                MenuPolicyTreeDto treeDto = new MenuPolicyTreeDto();
                BeanUtils.copyProperties(item, treeDto);
                treeDto.setPolicy(menuPolicy.get(item.getId()));
                findChildren(treeDto, item.getId(), menuParentMap, menuPolicy);
                return treeDto;
            }).collect(Collectors.toList()));
        }
    }

    @Override
    public List<AccessPolicy> getByRoles(List<Long> roleIds) {
        LambdaQueryWrapper<AccessPolicy> qw = new LambdaQueryWrapper<>();
        qw.in(AccessPolicy::getRoleId, roleIds);
        return list(qw);
    }

    @Override
    public UserInfoDto getCurrentUserPolicy() {
        UserInfoDto userInfoDto = new UserInfoDto();
        TeamUser user = userService.getCurrentUser();
        UserAuthDto userAuthDto = userService.getUserAuthorities(ContextUtil.getCurrentUser(), true);
        MenuPolicyTreeDto menuPolicyTreeDto = getUserMenuPolicyTree();
        BeanUtils.copyProperties(user, userInfoDto);
        userInfoDto.setGroups(userAuthDto.getGroups());
        if(CollectionUtils.isNotEmpty(userAuthDto.getRoles())){
            userInfoDto.setRoles(userAuthDto.getRoles());
        }
        userInfoDto.setMenuPolicyTreeDto(menuPolicyTreeDto);
        return userInfoDto;
    }


}




